package com.photoeditor.demo.model.store.module.preload;

import android.content.Context;
import android.text.TextUtils;

import com.common.base.ComponentContext;
import com.common.base.log.DLog;
import com.common.base.log.RLog;
import com.common.base.utils.AppUtils;
import com.common.base.utils.DebugUtil;
import com.common.base.utils.SystemUtils;
import com.photoeditor.demo.app.TestUser;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.photoeditor.demo.model.preference.SpUtils;
import com.photoeditor.demo.model.store.module.BaseOnlineManager;
import com.photoeditor.demo.util.AndroidDevice;
import com.photoeditor.demo.util.update.UpdateUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * Created by wu-mingguang on 2018/11/12.
 */
public class UpdateManager implements BaseOnlineManager.StoreOnlineListener{
    private static final boolean DEBUG = DebugUtil.isDebuggable() || TestUser.isTestUser();
    private static UpdateManager mInstance = null;
    private PreloadManager mPreloadManager;
    private String TAG = UpdateManager.class.getSimpleName();
    private static final Object LOCK = new Object();
    private boolean mIsFirstPreload = true;

    private UpdateManager() {
        mPreloadManager = PreloadManager.getInstance();
        mPreloadManager.addDataListener(this);
    }

    public synchronized static UpdateManager getInstance() {
        if (mInstance == null) {
            mInstance = new UpdateManager();
        }
        return mInstance;
    }

    @Override
    public void needUpdateData(boolean needUpdate) {
        if (!needUpdate) {
            return;
        }
        if (DEBUG) {
            RLog.d(TAG, "start preload...");
        }
        String config = mPreloadManager.getConfig(PreLoadConfig.CONFIG_UPDATE_ID);
        if (TextUtils.isEmpty(config)) {
            return;
        }
        //保存更新信息
        saveUpdateString(config);
        handleBlockUpdate();
    }

    /**
     * 收到升级信息后,先解析JSON，再保存到Preference
     */
    private void saveUpdateString(String updateBase64) {
        String updateString = new String(android.util.Base64.decode(updateBase64, android.util.Base64.CRLF));
        if (TextUtils.isEmpty(updateString)) {
            return;
        }
        updateString = updateString.replaceAll("\n", "").replaceAll("\t", "");
        Gson gson = new Gson();
        JsonObject gObj = gson.fromJson(updateString, JsonObject.class);
        JsonElement jEle = gObj.get("update");
        JsonArray list = jEle.getAsJsonArray();
        int size = list.size();
        if (size == 0) {
            return;
        }
        List<UpdateUtil.UpdateInfo> updateInfos = new ArrayList<>();
        for (int i=0; i<size; i++) {
            JsonElement data = list.get(i);
            if (data == null || data instanceof JsonNull) {
                continue;
            } else {
                String str = data.toString();
                Map<String, Object> map = gson.fromJson(str, Map.class);
                Map<String, String> result = new HashMap<>();

                Iterator<Map.Entry<String, Object>> entries = map.entrySet().iterator();
                while (entries.hasNext()) {
                    Map.Entry<String, Object> entry = entries.next();
                    if (entry.getValue() instanceof Double) {
                        result.put(entry.getKey(), String.valueOf(((Double)entry.getValue()).intValue()));
                    } else {
                        result.put(entry.getKey(), String.valueOf(entry.getValue()));
                    }

                }
                UpdateUtil.UpdateInfo.Builder builder = new UpdateUtil.UpdateInfo.Builder();
                builder.add(result);
                builder.add(UpdateUtil.UpdateInfo.TAG_VERSION, result.get(UpdateUtil.UpdateInfo.TAG_VERSION));
                builder.add(UpdateUtil.UpdateInfo.TAG_FREQUENCY, result.get(UpdateUtil.UpdateInfo.TAG_FREQUENCY));
                builder.add(UpdateUtil.UpdateInfo.TAG_MSGID, result.get(UpdateUtil.UpdateInfo.TAG_MSGID));
                UpdateUtil.UpdateInfo updateInfo = builder.build();
                updateInfos.add(updateInfo);
            }
        }
        if (updateInfos.isEmpty()) {
            return;
        }
        UpdateUtil.UpdateInfo validInfo = null;
        for (UpdateUtil.UpdateInfo updateInfo: updateInfos) {
            if (updateInfo != null && isUpdateInfoValid(updateInfo)) {
                validInfo = updateInfo;
                break;
            }
        }
        if (validInfo == null) {
            return;
        }
        UpdateUtil.saveUpdateInfo(validInfo);
    }

    /**
     * 判断当前环境是否满足升级要求
     */
    private boolean isUpdateInfoValid(UpdateUtil.UpdateInfo updateInfo) {
        String enableCountries = updateInfo.getStringInfo(UpdateUtil.UpdateInfo.TAG_ENABLE_COUNTRIES, "");
        String enableLanguages = updateInfo.getStringInfo(UpdateUtil.UpdateInfo.TAG_ENABLE_LANGUAGES, "");
        String enableVersion = updateInfo.getStringInfo(UpdateUtil.UpdateInfo.TAG_ENABLE_VERSION, "");

        Context context = ComponentContext.getContext();
        String country = SystemUtils.getCountry(context);
        boolean b1 = enableCountries.toUpperCase().contains(country) || TextUtils.equals("ALL", enableCountries.toUpperCase());

        Locale locale = context.getResources().getConfiguration().locale;
        String language = locale.getLanguage().toUpperCase();
        boolean b2 = enableLanguages.toUpperCase().contains(language) || TextUtils.equals("ALL", enableLanguages.toUpperCase());

        //国家和语言，或
        if (!(b1 || b2)) {
            if (!b1) {
                DLog.d(TAG, "国家不满足升级要求");
            }
            if (!b2) {
                DLog.d(TAG, "语言不满足升级要求");
            }
            return false;
        }

        String versionCode = AndroidDevice.getVersionCode(context);
        boolean b3 = isVersionSupported(enableVersion, "", versionCode);
        if (!b2) {
            DLog.d(TAG, "当前版本不满足升级要求");
        }
        return b3;
    }

    /**
     * 版本默认策略：ALL 支持
     * @param s1 格式 1,4,6,20+ ，表示支持版本为1、4、6及20以上
     * @param s2 格式 34,10+ ，表示不支持版本为34及30以上
     * @return 表示支持的版本为1、4、6,20~30，如果版本在此范围内，返回true
     */
    private boolean isVersionSupported(String s1, String s2, String version){
        if (TextUtils.isEmpty(version)) {
            return false;
        }
        if (TextUtils.isEmpty(s1) && TextUtils.isEmpty(s2)) {
            //默认不支持
            return false;
        }
        StringBuffer enableRange = new StringBuffer();
        StringBuffer disableRange = new StringBuffer();
        if (discreteCompare(s1, version, enableRange)) {
            //version在支持的散列版本中
            return true;
        }
        if (discreteCompare(s2, version, disableRange)) {
            //version在不支持的散列版本中
            return false;
        }
        int versionCode = Integer.valueOf(version);
        if (enableRange.length() < 1 ^ disableRange.length() < 1) {
            boolean enable = enableRange.length() > 0;
            return matchRange(enable ? enableRange.toString() : disableRange.toString(), enable, versionCode);
        } else {
            return matchBoth(enableRange.toString(), disableRange.toString(), versionCode);
        }
    }

    /**
     *
     * @return version在版本的散列点中，返回true
     */
    private boolean discreteCompare(String range, String version, StringBuffer sb){
        String separator = ",";
        String postfix = "+";
        if (!TextUtils.isEmpty(range)) {
            String[] enables = range.split(separator);
            if (enables != null && enables.length > 0) {
                int length = enables.length;
                for (int i = 0; i < length; i++) {
                    if (TextUtils.isEmpty(enables[i])) {
                        continue;
                    } else if (!enables[i].contains(postfix)) {
                        if (version.equals(enables[i])) {
                            return true;
                        }
                    } else {
                        sb.delete(0, sb.length());
                        sb.append(enables[i]);
                    }
                }
            }
        }
        return false;
    }

    private Pattern digital = Pattern.compile("[0-9]+\\+");

    /**
     * @param enable true-range为支持范围
     * @return true-version支持
     */
    private boolean matchRange(String range, boolean enable, int versionCode){
        if (digital.matcher(range).matches()) {
            String substring = range.substring(0, range.length() - 1);
            int rangeCode = Integer.valueOf(substring);
            return (versionCode < rangeCode) ^ enable;
        } else {
            return true;
        }
    }

    /**
     *
     */
    private boolean matchBoth(String enable, String disable, int versionCode){
        if (!digital.matcher(enable).matches()) {
            return matchRange(disable, false, versionCode);
        } else if (!digital.matcher(disable).matches()) {
            return matchRange(enable, true, versionCode);
        } else {
            int enableCode = Integer.valueOf(enable.substring(0, enable.length() - 1));
            int disableCode = Integer.valueOf(disable.substring(0, disable.length() - 1));
            if (versionCode == enableCode) {
                return true;
            }
            if (versionCode == disableCode) {
                return false;
            }
            if (enableCode < disableCode) {
                return Math.abs(versionCode - enableCode) + Math.abs(versionCode - enableCode) == (disableCode - enableCode);
            } else {
                return versionCode > enableCode;
            }
        }
    }

    private void handleBlockUpdate() {
        String config = PreloadManager.getInstance().getConfig("9999");
        if (!TextUtils.isEmpty(config)) {
            String[] vcodes = config.split(",");
            boolean block = false;
            for (String code : vcodes) {
                if (code.equals(String.valueOf(AppUtils.getVersionCode(ComponentContext.getContext())))) {
                    block = true;
                    break;
                }
            }
            SpUtils.obtain().save("block_se", block);
        }
    }
}
